Hilt 介绍 | MAD Skills
本文是 MAD Skills 系列中有关 Hilt 的第一篇文章!在本文中,我们将探讨依赖项注入 (DI) 对应用的重要性,以及 Jetpack 推荐的 Android DI 解决方案——Hilt。
Hilt
https://dagger.dev/hilt/
如果您更喜欢通过视频了解此内容,可以在此处查看:
在 Android 应用中,您可以通过遵循依赖项注入的原则,为良好的应用架构奠定基础。这有助于重用代码、易于重构、易于测试!更多关于 DI 的好处,请参阅: Android 中的依赖项注入。
Android 中的依赖项注入
https://developer.android.google.cn/training/dependency-injection
在项目中创建类的实例时,您可以通过提供及传递所需依赖项,手动处理依赖关系图。
但是每次都手动执行会增加模版代码并且容易出错。以 iosched 项目 (Google I/O 开源应用) 中的一个 ViewModel 为例,您能想象创建一个 FeedViewModel 所需的依赖项及传递依赖项需要多大的代码量吗?
class FeedViewModel(
private val loadCurrentMomentUseCase: LoadCurrentMomentUseCase,
loadAnnouncementsUseCase: LoadAnnouncementsUseCase,
private val loadStarredAndReservedSessionsUseCase: LoadStarredAndReservedSessionsUseCase,
getTimeZoneUseCase: GetTimeZoneUseCase,
getConferenceStateUseCase: GetConferenceStateUseCase,
private val timeProvider: TimeProvider,
private val analyticsHelper: AnalyticsHelper,
private val signInViewModelDelegate: SignInViewModelDelegate,
themedActivityDelegate: ThemedActivityDelegate,
private val snackbarMessageManager: SnackbarMessageManager
) : ViewModel(),
FeedEventListener,
ThemedActivityDelegate by themedActivityDelegate,
SignInViewModelDelegate by signInViewModelDelegate {
/* ... */
}
iosched
https://github.com/google/ioschedFeedViewModel
https://github.com/google/iosched/blob/main/mobile/src/main/java/com/google/samples/apps/iosched/ui/feed/FeedViewModel.kt
这是复杂且机械化的,并且我们很容易弄错依赖关系。依赖项注入库可以让我们利用 DI 的优势,而无需手动提供依赖关系,因为库会帮您生成所有需要的代码。这也就是 Hilt 发挥作用的地方。
Hilt
Hilt 是一个由 Google 开发的依赖项注入库,它通过处理复杂的依赖关系并为您生成原本需要手动编写的模版代码,帮助您在应用中充分利用 DI 的最佳实践。
Hilt 通过使用注解在编译期帮您生成代码,来保证运行时性能。这是利用 JVM DI 库 Dagger 的能力实现的,而 Hilt 是基于 Dagger 构建的。
Hilt
https://developer.android.google.cn/training/dependency-injection/hilt-androidDagger
https://developer.android.google.cn/training/dependency-injection/dagger-basics
快速开始
@HiltAndroidApp
class MusicApp : Application()
@AndroidEntryPoint
class PlayActivity : AppCompatActivity() { /* ... */ }
@AndroidEntryPoint
class PlayActivity : AppCompatActivity() {
@Inject lateinit var player: MusicPlayer
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(bundle)
player.play("YHLQMDLG")
}
}
class MusicPlayer @Inject constructor() {
fun play(id: String) { ... }
}
手动实现
// PlayActivity 已被添加 @AndroidEntryPoint 注解
class PlayActivityContainer {
// MusicPlayer 已被添加 @Inject 注解
fun provideMusicPlayer() = MusicPlayer()
}
class PlayActivity : AppCompatActivity() {
private lateinit var player: MusicPlayer
// 在 Activity 上添加 @AndroidEntryPoint 注解时由 Hilt 创建
private lateinit var container: PlayActivityContainer
override fun onCreate(savedInstanceState: Bundle) {
// @AndroidEntryPoint 同样为您创建并填充字段
container = PlayActivityContainer()
player = container.provideMusicPlayer()
super.onCreate(bundle)
player.play("YHLQMDLG")
}
}
注解回顾
至此,我们已经看见,当 @Inject 注解被添加到类的构造函数上时,它会告诉 Hilt 如何提供该类的实例。当变量被添加 @Inject 注解,并且变量所属的类被添加 @AndroidEntryPoint 注解时,Hilt 会向该类中注入一个相应类型的实例。
@AndroidEntryPoint 注解可以添加到绝大部分 Android 框架类上,不仅仅是 Activity。它会为被添加注解的类去创建一个依赖项容器的实例,并填充所有添加了 @Inject 注解的变量。
在 Application 类上添加 @HiltAndroidApp 注解,除了触发 Hilt 生成代码之外,还创建了一个与 Application 关联的依赖项容器。
Hilt 模块
class MusicPlayer @Inject constructor(
private val db: MusicDatabase
) {
fun play(id: String) { ... }
}
class PlayActivityContainer(val context: Context) {
fun provideMusicDatabase(): MusicDatabase {
return Room.databaseBuilder(
context, MusicDatabase::class.java, "music.db"
).build()
}
fun provideMusicPlayer() = MusicPlayer(
provideMusicDatabase()
)
}
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
@Provides
fun provideMusicDB(@ApplicationContext context: Context): MusicDatabase {
return Room.databaseBuilder(
context, MusicDatabase::class.java, "music.db"
).build()
}
}
Hilt 组件
△ 组件是一个 Hilt 生成的类,负责提供类型的实例
限定作用域
class PlayActivityContainer(val context: Context) {
fun provideMusicDatabase(): MusicDatabase {
return Room.databaseBuilder(
context, MusicDatabase::class.java, "music.db"
).build()
}
fun provideMusicPlayer() = MusicPlayer(
provideMusicDatabase()
)
}
class PlayActivityContainer {
val musicDatabase: MusicDatabase =
Room.databaseBuilder(
context, MusicDatabase::class.java, "music.db"
).build()
fun provideMusicPlayer() = MusicPlayer(musicDatabase)
}
@Module
@InstallIn(SingletonComponent::class)
object DataModule {
@Singleton
@Provides
fun provideMusicDB(@ApplicationContext context: Context): MusicDatabase {
return Room.databaseBuilder(
context, MusicDatabase::class.java, "music.db"
).build()
}
}
绑定
有两种类型的绑定:
未限定作用域绑定: 没有添加作用域注解的绑定,例如 MusicPlayer,如果它们没有被装载到模块中,则所有组件都可以使用这些绑定。
限定作用域绑定: 添加了作用域注解的绑定,例如 MusicDatabase,以及被装载到模块中的未限定作用域绑定,只有对应组件及其组件层次结构下方组件可以使用这些绑定。
Jetpack 扩展
@HiltViewModel
class FeedViewModel @Inject constructor(
private val loadCurrentMomentUseCase: LoadCurrentMomentUseCase,
loadAnnouncementsUseCase: LoadAnnouncementsUseCase,
private val loadStarredAndReservedSessionsUseCase: LoadStarredAndReservedSessionsUseCase,
getTimeZoneUseCase: GetTimeZoneUseCase,
getConferenceStateUseCase: GetConferenceStateUseCase,
private val timeProvider: TimeProvider,
private val analyticsHelper: AnalyticsHelper,
private val signInViewModelDelegate: SignInViewModelDelegate,
themedActivityDelegate: ThemedActivityDelegate,
private val snackbarMessageManager: SnackbarMessageManager
) : ViewModel(),
FeedEventListener,
ThemedActivityDelegate by themedActivityDelegate,
SignInViewModelDelegate by signInViewModelDelegate {
/* ... */
}
Hilt 和 Jetpack 集成
https://developer.android.google.cn/training/dependency-injection/hilt-jetpack?hl=zh-cn
iosched
https://github.com/google/iosched
FeedViewModel
https://github.com/google/iosched/blob/main/mobile/src/main/java/com/google/samples/apps/iosched/ui/feed/FeedViewModel.kt
了解更多
Dagger 基础知识
https://developer.android.google.cn/training/dependency-injection/dagger-basics 迁移到 Hilt 指南
http://dagger.dev/hilt/migration-guide
Hilt 及 Dagger 注解的区别及使用方式的备忘录
https://services.google.cn/fh/files/misc/hilt-annotations-v2.33_zh-cn.pdf
使用 Hilt 实现依赖项注入
https://developer.android.google.cn/training/dependency-injection/hilt-android
Codelab: 在 Android 应用中使用 Hilt
https://developer.android.google.cn/codelabs/android-hilt#0
欢迎您通过下方二维码向我们提交反馈,或分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!
推荐阅读